/*
 * Reksio - Memory Map Editor
 * Copyright (C) 2023 CERN
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * In applying this licence, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an Intergovernmental Organization or
 * submit itself to any jurisdiction.
 */

#include "treenodeoverviewmodel.h"
#include "memorynode.h"
#include "attribute.h"
#include "validatornode.h"
#include "attributecontainervalidator.h"
#include "attributevalidator.h"
#include "utils.h"

#include <ios>
#include <sstream>

TreeNodeOverviewModel::TreeNodeOverviewModel(const MemoryNode *parent_node, const std::string &child_type, QObject *parent):
    QAbstractTableModel (parent),
    root_node(parent_node),
    child_type(child_type)
{
    const auto& all_attributes = root_node->getValidator()->getChild(child_type)->getAttributes()->getAllAttributes();
    std::copy_if(all_attributes.begin(), all_attributes.end(), std::back_inserter(visible_attributes),
                 [](const auto& attribute)
    {
        return attribute->isVisible();
    });
}

int TreeNodeOverviewModel::rowCount(const QModelIndex &/*parent*/) const
{
    return static_cast<int>(std::count_if(root_node->getChildren().begin(), root_node->getChildren().end(), [this](const std::unique_ptr<MemoryNode>& child){
        return this->child_type == child->getType();
    }));
}

int TreeNodeOverviewModel::columnCount(const QModelIndex &/*parent*/) const
{
    return static_cast<int>(visible_attributes.size());
}

QVariant TreeNodeOverviewModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();
    if(role == Qt::DisplayRole || role == SortRole)
    {
        MemoryNode* node = static_cast<MemoryNode*>(index.internalPointer());
        const auto& requested_attribute = visible_attributes[static_cast<size_t>(index.column())];
        Attribute* attr = node->getAttributeContainer()->getAttribute(requested_attribute->getFullName());
        if(attr)
        {
            return getAttributeValue(attr, role);
        }
    }
    return QVariant();
}

QModelIndex TreeNodeOverviewModel::index(int row, int column, const QModelIndex &parent) const
{
    if(!hasIndex(row, column, parent))
        return QModelIndex();
    std::vector<MemoryNode*> children;
    // to optimize
    for(const auto& child: root_node->getChildren())
    {
        if(child->getType() == child_type)
            children.push_back(child.get());
    }
    MemoryNode* node = children[static_cast<size_t>(row)];
    return createIndex(row, column, node);
}

QVariant TreeNodeOverviewModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role == Qt::DisplayRole)
    {
        if(orientation == Qt::Horizontal)
        {
            return QString::fromStdString(join(visible_attributes[static_cast<size_t>(section)]->getFullName(), "/"));
        }
    }
    return QVariant();
}

QVariant TreeNodeOverviewModel::getAttributeValue(const Attribute* attr, int role) const
{
    const std::string& type = attr->getValidator()->getAttributeType();
    const std::string& str_val = attr->getValue();
    QString value = QString::fromStdString(str_val);
    if(type == "str")
        return value;
    else if (type == "bool")
    {
        value = value.toLower().trimmed();
        if(value == "true")
            return true;
        return false;
    }
    else if(type == "int" || type == "hex")
    {
        bool ok;
        const long long int number = value.toLongLong(&ok, 0); // can be hex or dec

        if(ok)
        {
            if (role != SortRole && type == "hex")
            {
                std::ostringstream result;
                result << "0x" << std::uppercase << std::hex << static_cast<uintptr_t>(number);
                return QString::fromStdString(result.str());
            }
            else
            {
                return number;
            }
        }
        else if (!value.size())
            return 0;
    }
    else if(type == "float")
    {
        bool ok;
        float number = value.toFloat(&ok);
        if(ok)
            return number;
        else if (!value.size())
            return 0.0;
    }
    return value;
}
